﻿using System;
using System.Collections.Generic;
using System.Text;
using System.AddIn;
using System.AddIn.Pipeline;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using WinSCP;
using System.Text.RegularExpressions;
using System.Globalization;
using Toolbox.Plugin.Interfaces;
using System.ComponentModel.Composition;

namespace Toolbox.Plugin.FileHandler
{
    [PluginAdressable("sftp"), PluginDescription("sftp file handler")]
    public class SftpFileHandler : FileHandler, IPluginFileHandler
    {
        private Session sSFTP;


        #region ISupportInitialize Members

        public override void EndInit()
        {
            base.EndInit();

            if (this.sSFTP == null || !this.sSFTP.Opened)
            {
                string[] u = this.Directory.UserInfo.Split(':');
                string[] p = u.Length > 0 ? u[0].Split(';') : new string[] { };

                SessionOptions o = new SessionOptions
                {
                    Protocol = Protocol.Sftp,
                    HostName = this.Directory.Authority,
                    UserName = p.Length > 0 ? p[0] : "Anonymous",
                    Password = u.Length > 1 ? u[1] : "Anonymous@st.com",
                    SshHostKey = GetFormatedFingerprint(p)
                };

                this.sSFTP = new Session();
                this.sSFTP.Open(o);
            }
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (this.sSFTP is Session)
            {
                this.sSFTP.Dispose();
            }
        }

        public string[] List()
        {
            List<string> l = new List<string>();

            if (this.sSFTP is Session && this.sSFTP.Opened)
            {
                List<RemoteFileInfo> rdi = new List<RemoteFileInfo>(this.sSFTP.ListDirectory(".").Files);

                RemoteFileInfo[] files = rdi.ToArray();

                if (files.Length > 0)
                {
                    FileHandler.GetDatePartition<RemoteFileInfo>(ref files, this.BatchSize > 0 ? this.BatchSize : files.Length, GetRemoteDates, this.CurrentDate);
                }

                foreach (RemoteFileInfo rfi in files)
                {
                    if (!rfi.IsDirectory && Regex.IsMatch(rfi.Name, Path.GetFileName(this.Directory.AbsolutePath)) && this.CurrentDate < rfi.LastWriteTime)
                    {
                        l.Add(rfi.Name);
                    }
                }

                if (files.Length > 0)
                {
                    this.CurrentDate = files[files.Length - 1].LastWriteTime;
                }

            }

            return l.ToArray();
        }


        public void Get(ref string[] files)
        {
            for (int i = 0; i < files.Length; i++)
            {
                this.Get(ref files[i]);
            }
        }

        public void Get(ref string file)
        {
            DirectoryInfo local = new DirectoryInfo(Path.GetTempPath());

            string remote = file;
            file = local.FullName + "\\" + remote;

            if (this.sSFTP is Session && this.sSFTP.Opened)
            {
                TransferOptions o = new TransferOptions();
                o.TransferMode = TransferMode.Binary;
                TransferOperationResult r = this.sSFTP.GetFiles(remote, file, false, o);
            }
            else
            {
                throw new InvalidOperationException("Source sftp session not open");
            }
        }

        public void Put(string[] files)
        {
            for (int i = 0; i < files.Length; i++)
            {
                this.Put(files[i]);
            }
        }

        public void Put(string file)
        {
            FileInfo local = new FileInfo(file);

            if (this.sSFTP is Session && this.sSFTP.Opened)
            {
                TransferOptions o = new TransferOptions();
                o.TransferMode = TransferMode.Binary;

                TransferOperationResult r = this.sSFTP.PutFiles(local.FullName, local.Name, false, o);
            }
            else
            {
                throw new InvalidOperationException("Target sftp session not open");
            }
        }

        #region Helpers

        private string GetFormatedFingerprint(params string[] parameters)
        {
            foreach (string p in parameters)
            {
                if (p.StartsWith("fingerprint=", true, null))
                {
                    int a = "fingerprint=".Length;
                    int b = p.Length - (16 * 3);

                    string[] h = p.Substring(a, b - a).Split(new char[] { '-' }, 2);

                    return (h.Length > 0 ? h[0] + "-" : "") + (h.Length > 1 ? h[1].Replace('-', ' ') : "") + " " + p.Substring(b + 1).Replace('-', ':');
                }
            }

            return null;
        }

        private static int CompareRemoteDates(RemoteFileInfo x, RemoteFileInfo y)
        {
            return DateTime.Compare(x.LastWriteTime, y.LastWriteTime);
        }

        private static DateTime GetRemoteDates(RemoteFileInfo x)
        {
            return x.LastWriteTime;
        }

        #endregion
    }
}
